/**
 * @author jiagnqifan
 * @since 2016-03-31
 */

var Models = require('../../../../../models');
var ProductConfig = Models.ProductConfig;
var Product = Models.Product;
var Workspace = Models.Workspace;
var Project = Models.Project;
var BuildConfig = Models.BuildConfig;

var logger = require('log4js').getLogger();
var EventProxy = require('eventproxy');
var tools = require('../../../../../common/tools.js');
var fs = require('fs');
var request = require('request');
var config = require('../../../../../config.json');
var url = require('url');
var builder_url = url.format(config.mk.builder);
var fileUtil = require('../../../../../common/file-util.js');
var build_workspaces_path = config.mk.build_workspaces_path;
var workspaces_path = config.workspace.workspeces_path;
var master_workspace_path = config.master_workspaces;
var dom4js = require('../../../../../common/dom4js.js');
var path = require('path');
var _ = require('lodash');


function getWorspaceFileName(workspace_name) {

}

//util methods

function tryCallback(callback, err, data) {
    if (err) {
        logger.error(err);
    }
    if ('function' == typeof callback) {
        callback(err, data);
    }
}


function setBuildState(build, state, callback) {
    logger.info('change state of ' + build._id + ' to ' + state);
    build.state = state;
    build.save(callback);
}


function getBuildWorkspaceFileNameFromWorkspaceName(workspace_name) {
    try {
        fs.accessSync(build_workspaces_path, fs.R_OK);
    } catch (e) {
        fs.mkdirSync(build_workspaces_path);
    }
    return path.join(build_workspaces_path, workspace_name);
}

function convertProductJSON2XML(jsonData) {

//prepare
    var dependencyConverters = {};
    dependencyConverters.lib = function (dependency, parent) {
        parent.addElement('group').setText(dependency.group);
        parent.addElement('artifact').setText(dependency.artifact);
        parent.addElement('version').setText(dependency.version);
    }
    dependencyConverters.product = function (dependency, parent) {
        parent.addElement('name').setText(dependency.name);
        parent.addElement('version').setText(dependency.version);
    }
//run
    var document = new dom4js.Document();
    var productEle = document.addElement('product');
    productEle.addElement('name').setText(jsonData.name);
    productEle.addElement('version').setText(jsonData.version);

    //convert dependencies
    var dependenciesEle = productEle.addElement('dependencies');
    var dependencies = jsonData.dependencies;
    dependenciesEle.addAttribute('include-packaging', dependencies['include-packaging']);
    var dependencyList = dependencies.list;
    dependencyList.forEach(function (dependency) {
        var type = dependency.type;
        var converter = dependencyConverters[type];
        if (!converter) {
            logger.error('未知的类型:' + type);
            return;
        }
        var element = dependenciesEle.addElement('dependency');
        element.addAttribute('type', type);
        converter(dependency, element);
    });


    //convert projects
    var projectsEle = productEle.addElement('projects');
    var projects = jsonData.projects;
    projects.forEach(function (project) {
        var element = projectsEle.addElement('project');
        element.addElement('id').setText(project.id);
        element.addElement('location').setText(project.location);
    });

    return document.asXML();
}

/**

 1. 准备工作空间
 1.1 复制工作空间
 1.1.1 找到项目信息
 1.2.1 判断该项目的构建工作空间是否可用
 1.2 写入product信息
 1.2.1 准备product信息
 1.2.2 写入工作空间中
 2. 发起构建

 PS: 用于开发的工作空间在更新之后，用于构建的工作空间也需要更新.
 */
function start(build, callback) {

    var ep = new EventProxy();

    //err handle
    ep.fail(function (message) {
        build.state_info = message;
        setBuildState(build, 'error');
        tryCallback(callback, message);
    });


    var project = null;

    ep.on('begin', function () {

        Project.findById(build.project, function (err, data) {
            if (err) {
                return ep.throw(err);
            }
            if (!data) {
                return ep.throw('can not find project:' + build.project + ' of build:' + build._id);
            }
            project = data;
            ep.emit('project_found');
        });

    });

    ep.on('project_found', function () {
        //set state to readying, then to start
        setBuildState(build, 'readying', function (err, data) {
            if (err) {
                return ep.throw('error occurs when change build state to readying.');
            }
            //messageBroker.emit('build_state_update',...)
            ep.emit('state_change_to_readying');
        });

    });


    function mkWorkspaceDirSync(workspace_path) {
        var source_path = path.join(workspace_path, 'source');
        fs.mkdirSync(source_path);
    }

    //保证temp_workspace中有对应的工作空间 而且在这个过程中不能有别的操作（同步）
    function ensureWorkspace(project_id, build_workspace_name, callback) {

        var build_workspace_path = getBuildWorkspaceFileNameFromWorkspaceName(build_workspace_name);
        try {
            fs.accessSync(build_workspace_path, fs.R_OK);
        } catch (e) {
            fs.mkdirSync(build_workspace_path);
            mkWorkspaceDirSync(build_workspace_path);
        }

        var ep = new EventProxy();
        ep.fail(function (err) {
            tryCallback(callback, err);
        });

        //有项目一定有master_workspace
        //Workspace.find({'project_id': project_id}, function (err, workspaces) {
        //    if (err) {
        //        return ep.throw(err);
        //    }
        //    if (!workspaces || workspaces.length < 1) {
        //        return ep.throw('无法找到项目:' + project_id + '的工作空间.');
        //    }
        //var workspace = workspaces[0];  //新模式下 不能是 workspace[0] 而应该是拉取下来的那个master workspace
        var workspace_file_name = project.name;
        var source_path = path.join(build_workspace_path, 'source');
        var dev_source_path = path.join(master_workspace_path,project.namespace,project.name, 'source');
        prepareSource(source_path, dev_source_path, ep.done('source_dir_ready'));
        var target_path = path.join(build_workspace_path, 'target');
        var dev_target_path = path.join(master_workspace_path,project.namespace,project.name, 'target');
        prepareTarget(target_path, dev_target_path, ep.done('target_dir_ready'));

        var eme_path = path.join(build_workspace_path, 'eme');
        var dev_eme_path = path.join(master_workspace_path, 'eme');
        //TODO::现在是手动放，以后直接从store下 这里检查eme emd 是否存在，不存在的话，初始化；
        try {
            fs.accessSync(dev_eme_path, fs.R_OK);
        } catch (e) {
            fs.mkdirSync(dev_eme_path);
        }
        copyDir(eme_path, dev_eme_path, ep.done('eme_dir_ready'));


        var emd_path = path.join(build_workspace_path, 'emd');
        var dev_emd_path = path.join(master_workspace_path, 'emd');
        try {
            fs.accessSync(dev_emd_path, fs.R_OK);
        } catch (e) {
            fs.mkdirSync(dev_emd_path);
        }
        copyDir(emd_path, dev_emd_path, ep.done('emd_dir_ready'));

        ep.all('source_dir_ready', 'target_dir_ready', 'eme_dir_ready', 'emd_dir_ready', function () {
            logger.info('workspace_ready.');
            tryCallback(callback, null, {
                name: build_workspace_name,
                path: build_workspace_path
            });
        });
        //});


    }

    function copyDir(build_path, dev_path, callback) {

        logger.info('copy file from[' + dev_path + '] to [' + build_path + ']');
        fileUtil.copy({
            src: dev_path,
            dest: build_path,
            filter: function (pathName) {
                var basename = path.basename(pathName);
                if (basename != '.DS_Store') {
                    return true;
                }
            }
        }, function () {

            logger.info('done:' + dev_path);
            callback();
        });
    }



    function prepareSource(source_path, dev_source_path, callback) {
        //copy source 前应该执行合并 准备target等

        logger.info('copy file from[' + dev_source_path + '] to [' + source_path + ']');
        fileUtil.copy({
            src: dev_source_path,
            dest: source_path,
            filter: function (pathName) {
                var basename = path.basename(pathName);
                if (basename != '.DS_Store') {
                    return true;
                }
            }
        }, function () {

            logger.info('done:source');
            callback();
        });


    }

    function prepareTarget(target_path, dev_target_path, callback) {
        logger.info('copy file from[' + dev_target_path + '] to [' + target_path + ']');
        fileUtil.copy({
            src: dev_target_path,
            dest: target_path,
            filter: function (pathName) {
                var basename = path.basename(pathName);
                if (basename != '.DS_Store') {
                    return true;
                }
            }
        }, function () {
            logger.info('done: target');
            callback();
        });
    }

    //workspace_prepared
    ep.on('state_change_to_readying', function ready() {

        logger.info('state_change_to_readying');
        var getAllProjects = require('../../../workspace/workspace.js').getAllProjects;
        getAllProjects(project._id,function(err){
            if(err){
                return ep.throw(err);
            }else{
                //to ensureworkspace 确认
                ensureWorkspace(build.project, project.name, function (err, build_workspace) {
                    if (err) {
                        return ep.throw(err);
                    }
                    ep.emit('workspace_prepared', build_workspace);
                });
                //find project dir from product config
            }
        });


    });

    //write product info
    ep.on('workspace_prepared', function (build_workspace) {

        logger.info('workspace_prepared.');

        var build_workspace_path = build_workspace.path;
        var workspace_name = build_workspace.name;
        var repo_name = project.name;
        var repo_path = path.join(build_workspace_path, 'source', repo_name);
        var product_dir_path = path.join(repo_path, '.product');

        try {
            fs.accessSync(product_dir_path, fs.R_OK);
            //删除product_dir_path下的所有文件和文件夹
            tools.rmdirAllSync(product_dir_path);
            fs.mkdirSync(product_dir_path);
        } catch (e) {
            fs.mkdirSync(product_dir_path);
        }

        var config = build.config;
        var product_config_ids = config.product_configs;

        product_config_ids.forEach(function (product_config_id) {
            ProductConfig.findById(product_config_id, function (error, product_config) {
                if (error) {
                    return ep.throw(error);
                }
                if (!product_config) {
                    return ep.throw('can not find product_config:' + product_config_id);
                }
                var detail_info = product_config.detail;
                var detail_projects = [];
                product_config.projects.map(function (project_info) {
                    detail_projects.push(
                        {
                            "id": project_info.name,
                            "location": _.drop(project_info.location.split(path.sep), 3).join(path.sep)
                        }
                    );
                });
                var detail = {
                    'dependencies': detail_info.dependencies,
                    'version': product_config.version,
                    'name': product_config.name,
                    'projects': detail_projects,
                };
                var xml = convertProductJSON2XML(detail);
                var dir = path.join(product_dir_path, product_config.name);
                try {
                    fs.accessSync(dir, fs.R_OK);
                } catch (e) {
                    fs.mkdirSync(dir);
                }
                fs.writeFileSync(path.join(dir, 'product.xml'), xml);
            });
        });

        // var product_config_ids = build.config.product_configs;
        // if(!product_config_ids || !product_config_ids.length){
        //   var message = 'cannot start build because no product config.';
        //   return tryCallback(callback, message);
        // }
        // var product_config_id = product_config_ids[0];
        ep.emit('product_info_prepared', workspace_name);
    });

    //post build task
    ep.on('product_info_prepared', function (workspaceName) {

        logger.info('product_info_prepared.');
        var url = builder_url + '/build/progress?workspaceName=' + workspaceName

        logger.info('post: ' + url);
        request({
            method: 'POST',
            url: url,
            // body:{
            //   workspaceName: workspaceName
            // },
            json: true
        }, function (err, response, body) {
            if (err) {
                return ep.throw(err);
            }
            if (body.retCode) {
                return ep.throw(body.progressId);
            }
            var progressId = body.progressId;
            if (!progressId) {
                return ep.throw('no progressId return.');
            } else {
                ep.emit('build_task_posted', progressId);
            }
        });

    });

    //change state to building
    ep.on('build_task_posted', function (progressId) {

        logger.info('build_task_posted.');

        build.state = 'building';
        build.token = progressId;
        build.save(function (err, data) {
            if (err) {
                return ep.throw(err);
            }
            ep.emit('state_change_to_building');
        })
    });

    ep.on('state_change_to_building', function () {
        //messageBroker.emit('build_state_update',...)
        logger.info('state_change_to_building.');
        tryCallback(callback);
    });

    ep.emit('begin');
}


function complete(build, callback) {

    var ep = new EventProxy();

    ep.fail(function (err) {
        logger.error(err);
        tryCallback(callback, err);
    });

    ep.on('begin', function () {
        logger.info('complete:begin');
        getResult(build.token, function (err, result) {
            var state = result.state;
            build.result = state;
            build.result_detail = result;
            build.markModified('result_detail');
            build.save(function (err, data) {
                //messageBroker.emit('build_state_update',...)
                if (state == 'SUCCESS') {
                    ep.emit('result_geted', result);
                } else {
                    ep.emit('end', false);
                }
            });
        });
    });

    // add products
    ep.on('result_geted', function (result) {
        logger.info('complete:result_geted');
        var files = result.files;
        var product_config_ids = build.config.product_configs;

        ProductConfig.find({_id: {$in: product_config_ids}}, function (err, product_configs) {
            if (err) {
                return ep.throw(err);
            }
            var product_config_map = {};
            product_configs.forEach(function (product_config) {
                product_config_map[product_config.name] = product_config;
            });
            files.forEach(function (file) {
                var product_config = product_config_map[file.name];
                if (product_config) {
                    var product = new Product();
                    var config = product_config_map[file.name];
                    if (config) {
                        product.config = config._id;
                    }
                    product.version = product_config.version;
                    product.url = file.location.url || file.location.uri;
                    product.save(ep.done('product_created'));
                } else {
                    console.error('ignore product: ' + file.name);
                }

            });

            ep.after('product_created', files.length, function () {
                ep.emit('end');
            });
        });
    });


    ep.on('end', function (isSuccess) {
        logger.info('complete:end');
        setBuildState(build, 'completed', callback);
        // tryCallback(callback);
    })


    build.state = 'completing';
    build.save(function (err, data) {
        if (err) {
            return ep.throw('err occurs when change state of [' + build._id + '] to completing.');
        }
        //messageBroker.emit('build_state_update',...)
        ep.emit('begin');
    });

}

function getResult(progressId, callback) {
    var url = builder_url + '/build/result?progressId=' + progressId;
    request(url, function (err, response, body) {
        if (!err && response.statusCode == 200) {
            var bodyJSON = JSON.parse(body);
            var results = bodyJSON.result;
            callback(null, results[0]);
        } else {
            callback(err ? err : 'statusCode:' + response.statusCode);
        }
    });
}


function cancel(build, callback) {
    build.state = 'canceled';
    build.save(callback);
}

function getProgress(build, callback) {
    getProgressData(build.token, function (err, progress) {
        if (err) {
            return callback(err);
        }
        if (!progress) {
            return callback('查询构建状态失败');
        }
        callback(null, progress.progress);
    });
}

function tryComplete(build, callback) {
    getIsComplete(build, function (err, completed) {
        logger.info(build._id + ' completed:' + completed);
        if (err) {
            logger.error(err);
            if ('function' === typeof callback) {
                callback(err);
            }
            return;
        }

        if (completed) {
            complete(build, callback);
        }
    })
}

function getProgressData(progressId, callback) {
    var url = builder_url + '/build/progress?progressId=' + progressId;
    request(url, function (err, response, body) {
        if (!err && response.statusCode == 200) {
            var bodyJSON = JSON.parse(body);
            var progresses = bodyJSON.progress;
            callback(null, progresses ? progresses[0] : null);
        } else {
            callback(err ? err : 'statusCode:' + response.statusCode);
        }
    });
}

//check is comlete form builder server
function getIsComplete(build, callback) {
    getProgressData(build.token, function (err, progress) {
        if (err) {
            return callback(err);
        }
        if (!progress) {
            return callback('查询构建状态失败.')
        }
        callback(null, 'FINISHED' == progress.state || 'ERROR' == progress.state);
    });
}


exports.start = start;
exports.cancel = cancel;
exports.getProgress = getProgress;
exports.tryComplete = tryComplete;
